﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using Daphne.SharedMetro;

namespace Daphne.ControlsMetro.Clock
{
    /// <summary>
    /// Class managing measuring clock time for the game
    /// TO-DO: Move to the middle layer module
    /// Study delails of multiple records of a certain number of minutes 
    /// per a given number of moves
    /// </summary>
    internal class Clock: IClock
    {
        /// <summary>
        /// Value for minutes to check overflow
        /// </summary>
        private const int _overflowTreshold = 99;


        /// <summary>
        /// The clock attributes
        /// </summary>
        private Stopwatch _whiteStopWatch;
        private Stopwatch _blackStopWatch;
        private TimeSpan _whiteMoveStarted;
        private TimeSpan _blackMoveStarted;
        private TimeSpan _whiteGameTime;
        private TimeSpan _blackGameTime;
        private int _whiteClockIndex;
        private int _blackClockIndex;

        /// <summary>
        /// Type of measuring
        /// </summary>
        private ClockSettings _settings;

        /// <summary>
        /// Move counters
        /// </summary>
        private int _whiteMoveCount;
        private int _blackMoveCount;

        /// <summary>
        /// Currently measured time
        /// </summary>
        private eClockSTM _measuredSTM;

        /// <summary>
        /// Constructor
        /// </summary>
        public Clock()
        {
            _whiteStopWatch = new Stopwatch();
            _blackStopWatch = new Stopwatch();
        }
       
        /// <summary>
        /// Initializes the clock
        /// </summary>
        public void Init(ClockSettings clockSettings)
        {
            _settings = clockSettings;
            _whiteStopWatch.Reset();
            _blackStopWatch.Reset();
            _whiteMoveCount = 0;
            _blackMoveCount = 0;
            _measuredSTM = eClockSTM.White;

            if(_settings.ClockType == eClockType.ControlForGame)
            {
                _whiteGameTime = _blackGameTime = TimeSpan.FromMinutes(_settings.MinutesForGame);
            }
            else if(_settings.ClockType == eClockType.ControlForMove)
            {
                _whiteClockIndex = 0;
                _blackClockIndex = 0;
                _whiteGameTime = _blackGameTime = TimeSpan.FromMinutes(_settings.MinutesForMove[0].Minutes);
            }
        }

        #region Clock Manipulation Methods
        
        /// <summary>
        /// Starts the white's clock
        /// </summary>
        public void Start()
        {
            _whiteStopWatch.Start();
            _whiteMoveStarted = new TimeSpan(0);
        }

        /// <summary>
        /// Stops the clock
        /// </summary>
        public void Stop()
        {
            Pause();           
        }

        /// <summary>
        /// Pauses the clock
        /// </summary>
        public void Pause()
        {
            if(_measuredSTM == eClockSTM.White)
            {
                _whiteStopWatch.Stop();
            }
            else
            {
                _blackStopWatch.Stop();
            }
        }

        /// <summary>
        /// Resumes the clock
        /// </summary>
        public void Resume()
        {
            if(_measuredSTM == eClockSTM.White)
            {
                _whiteStopWatch.Start();
            }
            else
            {
                _blackStopWatch.Start();
            }
        }

        /// <summary>
        /// Switches the clock
        /// </summary>
        public void ChangeSTM()
        {
            if(_whiteStopWatch.IsRunning)
            {
                // white's move finished
                _whiteStopWatch.Stop();

                _whiteMoveCount++;
                _measuredSTM = eClockSTM.Black;
                _blackMoveStarted = AdjustTime(_blackStopWatch.Elapsed);
                _blackStopWatch.Start();

                // change clock settings, if necessary
                if(_settings.ClockType == eClockType.ControlForMove)
                {
                    if(_whiteClockIndex < _settings.MinutesForMove.Length)
                    {
                        if(_whiteMoveCount >= _settings.MinutesForMove[_whiteClockIndex].Moves)
                        {
                            _whiteClockIndex++;
                            int minutes = 0;
                            if(_whiteClockIndex == _settings.MinutesForMove.Length)
                            {
                                minutes = _settings.MinutesToFinishGame ?? 0;
                            }
                            else
                            {
                                minutes = _settings.MinutesForMove[_whiteClockIndex].Minutes;
                            }

                            _whiteGameTime += TimeSpan.FromMinutes(minutes);
                        }
                    }
                }
            }
            else
            {
                // black's move finished
                _blackStopWatch.Stop();

                _blackMoveCount++;
                _measuredSTM = eClockSTM.White;
                _whiteMoveStarted = AdjustTime(_whiteStopWatch.Elapsed);
                _whiteStopWatch.Start();

                if (_settings.ClockType == eClockType.ControlForMove)
                {
                    if (_blackClockIndex < _settings.MinutesForMove.Length)
                    {
                        if (_blackMoveCount >= _settings.MinutesForMove[_blackClockIndex].Moves)
                        {
                            _blackClockIndex++;
                            int minutes = 0;
                            if (_blackClockIndex == _settings.MinutesForMove.Length)
                            {
                                minutes = _settings.MinutesToFinishGame ?? 0;
                            }
                            else
                            {
                                minutes = _settings.MinutesForMove[_blackClockIndex].Minutes;
                            }

                            _blackGameTime += TimeSpan.FromMinutes(minutes);
                        }
                    }

                }
            }
        }

        /// <summary>
        /// Adjusts time for the result, corrects time underflow and overflow
        /// </summary>
        private TimeSpan AdjustTime(TimeSpan span)
        {
            // if underflow, return zero timespan
            if (span.Ticks < 0)
                return TimeSpan.FromMilliseconds(0);

            // calculate minutes and seconds
            int minutes = span.Hours * 60 + span.Minutes;
            int seconds = span.Seconds;

            // check overflow
            if (minutes > _overflowTreshold)
                minutes = _overflowTreshold;

            // return the result
            return TimeSpan.FromSeconds(minutes * 60 + seconds);
        }

        #endregion

        #region Returns Clock Measurements
        
        public TimeSpan WhiteMoveTime
        {
            get { return AdjustTime(_whiteStopWatch.Elapsed - _whiteMoveStarted); }
        }

        public TimeSpan BlackMoveTime
        {
            get { return AdjustTime(_blackStopWatch.Elapsed - _blackMoveStarted); }
        }

        public TimeSpan WhiteGameTime
        {
            get 
            {
                if (_settings.ClockType == eClockType.NoControl)
                    return AdjustTime(_whiteStopWatch.Elapsed);
                else
                    return AdjustTime(_whiteGameTime - _whiteStopWatch.Elapsed);
            }
        }

        public TimeSpan BlackGameTime
        {
            get 
            {
                if (_settings.ClockType == eClockType.NoControl)
                    return AdjustTime(_blackStopWatch.Elapsed);
                else
                    return AdjustTime(_blackGameTime - _blackStopWatch.Elapsed);
            }
        }

        #endregion
    }
}
